﻿using Acme.BookStore.Authors;
using Acme.BookStore.Permissions;
using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;
using System.Linq.Dynamic.Core;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;

namespace Acme.BookStore.Books
{
    [Authorize(BookStorePermissions.Books.Default)]
    public class BookAppService : 
        CrudAppService<
            Book, 
            BookDto, 
            Guid, 
            PagedAndSortedResultRequestDto, 
            CreateUpdateBookDto>, 
        IBookAppService
    {
        private readonly IAuthorRepository _authorRepository;
        public BookAppService(
            IRepository<Book, Guid> repository,
            IAuthorRepository authorRepository)
            : base(repository)
        {
            _authorRepository = authorRepository;
            GetPolicyName = BookStorePermissions.Books.Default;
            GetListPolicyName = BookStorePermissions.Books.Default;
            CreatePolicyName = BookStorePermissions.Books.Default;
            UpdatePolicyName  = BookStorePermissions.Books.Default;
            DeletePolicyName = BookStorePermissions.Books.Default;
        }

        public async override Task<BookDto> GetAsync(Guid id)
        {
            var book = await Repository.GetAsync(id);
            var bookDto=ObjectMapper.Map<Book,BookDto>(book);

            var author=await _authorRepository.GetAsync(book.AuthorId);
            bookDto.AuthorName = author.Name;

            return bookDto;
        }

        public async override Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
        {
            if (input.Sorting.IsNullOrWhiteSpace())
            {
                input.Sorting = nameof(Book.Name);
            }

            var queryable = await Repository.GetQueryableAsync();

            var books = await AsyncExecuter.ToListAsync(
                queryable.OrderBy(input.Sorting)
                .Skip(input.SkipCount)
                .Take(input.MaxResultCount)
                );

            var bookDto = ObjectMapper.Map<List<Book>, List<BookDto>>(books);

            var authorDictionary = await GetAuthorDictionaryAsync(books);

            bookDto.ForEach(bookDto => bookDto.AuthorName = authorDictionary[bookDto.AuthorId].Name);

            var totalCount=await Repository.GetCountAsync();

            return new PagedResultDto<BookDto>(totalCount,bookDto
                );
        }

        public async Task<ListResultDto<AuthorLookupDto>> GetAuthorLookupAsync()
        {
            var authors = await _authorRepository.GetListAsync();

            return new ListResultDto<AuthorLookupDto>(
                ObjectMapper.Map<List<Author>, List<AuthorLookupDto>>(authors)
                );
        }

        private async Task<Dictionary<Guid,Author>>
            GetAuthorDictionaryAsync(List<Book> books)
        {
            var authorIds = books.Select(b => b.AuthorId).Distinct().ToArray();

            var queryable = await _authorRepository.GetQueryableAsync();

            var authors = await AsyncExecuter.ToListAsync(
                queryable.Where(a => authorIds.Contains(a.Id))
                );

            return authors.ToDictionary(x => x.Id, x => x);
        }
    }
}
